package nl.saxion.security.synchronize;

import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.crypto.SealedObject;

import nl.saxion.security.encryption.TransformerAES;
import nl.saxion.security.synchronize.Code;
import nl.saxion.security.transfer.Client;

/**
 * Class for ClientSynchronize
 *
 * @author EII7SCb
 *
 */
public class ClientSynchronize extends Synchronize {

	/**
	 * Synchronize for client side
	 * @param directory
	 * @param myIp
	 * @param fileList
	 * @param sealedKey
	 * @param pass
	 */
	public ClientSynchronize(String directory, String myIp, ArrayList<FileIncHash> fileList, SealedObject sealedKey, byte[] pass)
	{
		
		super((new Client(2004,2005, myIp)), directory, sealedKey, pass);
		this.fileList = fileList;
	}

	/**
	 * 
	 * @return
	 */
	public ArrayList<FileIncHash> getFileList()
	{
		return fileList;
	}
	
	/**
	 * 
	 * @param fileList2
	 */
	public void setFileList(ArrayList<FileIncHash> fileList2)
	{
		if(!busySync){
			compareFileList(fileList2);
		}
	}

	/**
	 * Sync keys with server
	 */
	protected void syncKey() throws Exception
	{
		sendCode(Code.HELLO);
		System.out.println("hello verstuurd!");
		waitConfirm(Code.CONFIRM); 
		System.out.println("confirm ontvangen");
		transfer.sendObject(sealedKey);
		System.out.println("Key verstuurd!");
		waitConfirmEncrypt(Code.CONFIRM); 
		transfer.dataCon.close();
		sendCodeEncrypt(Code.CONFIRM);	
		System.out.println("Key sync done!");
	}
	
	
	/**
	 * Encrypts files
	 * @param file
	 * @param newfile
	 * @throws Exception
	 */
	public ArrayList<FileIncHash> encryptFiles(File[] files, String newlocation) throws Exception 
	{
		ArrayList<FileIncHash> list = new ArrayList<FileIncHash>();
		
		for (File file : files) {
	
	    		String location = newlocation + "/" + file.getName();
				String cleanHash = transformer.generateHash(file.getAbsolutePath());
				Long modiDate = file.lastModified();
				
	    		transformer.encryptFile(getKey(), file.toString(), location);
	    		
	    	 	FileIncHash fileIncHash = new FileIncHash(location);
		    	fileIncHash.setDecryptedHash(cleanHash);
		    	fileIncHash.setEncryptedHash(transformer.generateHash(fileIncHash.getAbsolutePath()));
		    	fileIncHash.setLastModifiedDate(modiDate);
		    	list.add(fileIncHash);
		    	file.delete();
		}
		return list;

	}
	
	
	
	/**
	 * Start communication server
	 * @throws Exception 
	 */
	public void listening() throws Exception 
	{

		while(transfer.comCon == null || !transfer.comCon.isConnected())
		{
			try {
				transfer.createComConnection();
			} catch (Exception e) {
				//System.out.println("nog een keer proberen!");
			}
		}
		
		System.out.println("Connection");
		
		syncKey();
		
		while(getListening())
		{

			Thread.sleep(200);
			
			if(transfer.comCon.dataOnLine()){
				SealedObject o = (SealedObject) transfer.comCon.getObject();
				Code code = (Code)transformer.decryptObject(getKey(), o);

				System.out.println("Server > " + code.toString() );

				switch(Code.valueOf(code.toString()))	{
				case SYNC:{
					sendCodeEncrypt(Code.CONFIRM);
				}
					break;
				case START_SYNC:{
					startSynchronize();
				}
					break;
				case GETFILEPROPERTIES:{
					getFilesProperties();
				}
					break;
				case GETFILES:{
					sendFilesProperties();
				}
					break;
				case SENDFILES:{
					recieveFileProperties();
				}
					break;
				case CONFIRM:{
					System.out.println("Confirm FOUT");
				}
					break;	
				case GETTIME:{
					sendCurrentTime();	
					
				}
					break;
				case SYNC_DONE:{
					System.out.println("Sync klaar!");
					busySync = false;
				}
					break;
				}
			}
			
			if(getSync() && !busySync)
			{
				startSynchronize();
				busySync = true;
				sync = false;
			}
				
		} 	  
	}
	
	/**
	 * Send device time
	 * @throws Exception
	 */
	private void sendCurrentTime() throws Exception
	{
		Date date = new Date();
		transfer.sendObject(transformer.encryptObject(getKey(), date));
		waitConfirmEncrypt(Code.TIME_RECIEVED);
		sendCodeEncrypt(Code.CONFIRM);
		transfer.dataCon.close();
	}
	
	/**
	 * recieve file form other device
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private void recieveFileProperties() throws Exception 
	{
		sendCodeEncrypt(Code.CONFIRM);
		toReceiveFiles = (ArrayList<FileIncHash>)transformer.decryptObject(getKey(), (SealedObject) transfer.getObject());
		sendCodeEncrypt(Code.READY_FOR_SENDING);	
		receiveFiles();
	}

	/**
	 * Send files to other device
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private void sendFilesProperties() throws Exception
	{
		sendCodeEncrypt(Code.CONFIRM);
		toSendFiles =  (ArrayList<FileIncHash>)transformer.decryptObject(getKey(), (SealedObject) transfer.getObject());
		sendCodeEncrypt(Code.READY_FOR_SENDING);	
		sendFiles(toSendFiles);
		sendCodeEncrypt(Code.FILERECEIVED);	
	}
	
	/**
	 *  Send list with local files and the delete files
	 * @throws Exception
	 */
	private void getFilesProperties() throws Exception 
	{
		ArrayList<FileIncHash> androidFiles = getLocalFilesIncHash();
		ArrayList<FileIncHash> delFiles = checkForDels(androidFiles);
		
		fileList.removeAll(delFiles); 
		delFiles.addAll(fileList); 
		
		transfer.sendObject(transformer.encryptObject(getKey(),delFiles));
		waitConfirmEncrypt(Code.FILESPROPERTIES_RECIEVED);
		transfer.dataCon.close();
		sendCodeEncrypt(Code.CONFIRM);	
	}
	
	/**
	 * Send Files to other device over dataconnection.
	 * @param localFilesToSend
	 * @throws Exception
	 */
	protected void sendFiles(ArrayList<FileIncHash> localFilesToSend) throws Exception
	{
		for (FileIncHash file : localFilesToSend)
		{	
			if(!file.isDelete()){
				waitConfirmEncrypt(Code.CONFIRM);
				transfer.sendFile(file);
				waitConfirmEncrypt(Code.FILERECEIVED);
				System.out.println("File send: "+ file.getName());
				transfer.dataCon.close();	
			}
		}
	}
	
	/**
	 * Receive files form other device according to the set 'toReceiveFiles'
	 * @throws Exception
	 */
	protected void receiveFiles() throws Exception
	{
		for (FileIncHash file : toReceiveFiles)
		{
			String location = directory.getPath()+ "/" + file.getName();

			if(!file.isDelete()){
				waitConfirmEncrypt(Code.CONFIRM);
				transfer.getFile(location);
				sendCodeEncrypt(Code.FILERECEIVED);

				FileIncHash newFile = new FileIncHash(location); 
				newFile.setEncryptedHash(file.getEncryptedHash());
				newFile.setDecryptedHash(file.getDecryptedHash());
				newFile.setLastModifiedDate(file.getLastModifiedDate());
				this.fileList.add(newFile);
				
				System.out.println("File received: "+ file.getName());
			}
			else{
				File localfile = new File(location);
				Iterator<FileIncHash> localfiles2 = fileList.iterator();
				
		        while(localfiles2.hasNext()){
		        	FileIncHash localfile2 = localfiles2.next();
		        	if(localfile2.getName().equals(localfile.getName()))
		        		localfiles2.remove();
		        }
		        localfile.delete();
			}	
		}
	}
	

	/**
	 * Start synchronize
	 */
	public void startSynchronize() throws Exception {
		sendCodeEncrypt(Code.START_SYNC);
	}
	
	
	
}
